{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Multi-agent supervisor\n",
    "\n",
    "We can to use an LLM to orchestrate the different agents.\n",
    "\n",
    "Below, we will create an agent group, with an agent supervisor to help delegate tasks.\n",
    "\n",
    "## Agentic Architecture\n",
    "\n",
    "![original image]()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "var userHomeDir = System.getProperty(\"user.home\");\n",
    "var localRespoUrl = \"file://\" + userHomeDir + \"/.m2/repository/\";\n",
    "var langchain4jVersion = \"1.0.1\";\n",
    "var langchain4jbeta = \"1.0.1-beta6\";\n",
    "var langgraph4jVersion = \"1.6-SNAPSHOT\";"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash \n",
    "rm -rf \\{userHomeDir}/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/org/bsc/langgraph4j"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%dependency /add-repo local \\{localRespoUrl} release|never snapshot|always\n",
    "// %dependency /list-repos\n",
    "%dependency /add org.slf4j:slf4j-jdk14:2.0.9\n",
    "%dependency /add org.bsc.langgraph4j:langgraph4j-core:\\{langgraph4jVersion}\n",
    "%dependency /add org.bsc.langgraph4j:langgraph4j-langchain4j:\\{langgraph4jVersion}\n",
    "%dependency /add dev.langchain4j:langchain4j:\\{langchain4jVersion}\n",
    "// %dependency /add dev.langchain4j:langchain4j-open-ai:\\{langchain4jVersion}\n",
    "%dependency /add dev.langchain4j:langchain4j-ollama:\\{langchain4jbeta}\n",
    "%dependency /add net.sourceforge.plantuml:plantuml-mit:1.2024.8\n",
    "\n",
    "%dependency /resolve"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**utility to render graph respresentation in PlantUML**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "import net.sourceforge.plantuml.SourceStringReader;\n",
    "import net.sourceforge.plantuml.FileFormatOption;\n",
    "import net.sourceforge.plantuml.FileFormat;\n",
    "import org.bsc.langgraph4j.GraphRepresentation;\n",
    "\n",
    "void displayDiagram( GraphRepresentation representation ) throws IOException { \n",
    "    \n",
    "    var reader = new SourceStringReader(representation.getContent());\n",
    "\n",
    "    try(var imageOutStream = new java.io.ByteArrayOutputStream()) {\n",
    "\n",
    "        var description = reader.outputImage( imageOutStream, 0, new FileFormatOption(FileFormat.PNG));\n",
    "\n",
    "        var imageInStream = new java.io.ByteArrayInputStream(  imageOutStream.toByteArray() );\n",
    "\n",
    "        var image = javax.imageio.ImageIO.read( imageInStream );\n",
    "\n",
    "        display(  image );\n",
    "\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Initialize Log**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "try( var file = new java.io.FileInputStream(\"./logging.properties\")) {\n",
    "    java.util.logging.LogManager.getLogManager().readConfiguration( file );\n",
    "}\n",
    "\n",
    "var log = org.slf4j.LoggerFactory.getLogger(\"multi-agent-supervisor\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Graph State**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "import org.bsc.langgraph4j.prebuilt.MessagesState;\n",
    "import dev.langchain4j.data.message.ChatMessage;\n",
    "import java.util.Optional;\n",
    "\n",
    "class State extends MessagesState<ChatMessage> {\n",
    "\n",
    "    public Optional<String> next() {\n",
    "        return this.value(\"next\");\n",
    "    }\n",
    "\n",
    "    public State(Map<String, Object> initData) {\n",
    "        super( initData  );\n",
    "    }\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Create Serializer**\n",
    "\n",
    "This is important for supporting persistent state across graph execution"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "import org.bsc.langgraph4j.langchain4j.serializer.std.ChatMesssageSerializer;\n",
    "import org.bsc.langgraph4j.langchain4j.serializer.std.ToolExecutionRequestSerializer;\n",
    "import org.bsc.langgraph4j.serializer.std.ObjectStreamStateSerializer;\n",
    "import dev.langchain4j.agent.tool.ToolExecutionRequest;\n",
    "\n",
    "class StateSerializer extends ObjectStreamStateSerializer<State> {\n",
    "\n",
    "    public StateSerializer() {\n",
    "        super(State::new);\n",
    "\n",
    "        mapper().register(ToolExecutionRequest.class, new ToolExecutionRequestSerializer());\n",
    "        mapper().register(ChatMessage.class, new ChatMesssageSerializer());\n",
    "    }\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Supervisor Agent\n",
    "\n",
    "⚠️ Since this is just a POC the tool is mocked ⚠️"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "import dev.langchain4j.agent.tool.P;\n",
    "import dev.langchain4j.agent.tool.Tool;\n",
    "import dev.langchain4j.model.output.structured.Description;\n",
    "import dev.langchain4j.service.V;\n",
    "import dev.langchain4j.service.SystemMessage;\n",
    "import dev.langchain4j.service.AiServices;\n",
    "import dev.langchain4j.data.message.AiMessage;\n",
    "import dev.langchain4j.data.message.UserMessage;\n",
    "import dev.langchain4j.model.chat.ChatModel;\n",
    "import org.bsc.langgraph4j.action.NodeAction;\n",
    "import java.util.concurrent.CompletableFuture;\n",
    "import static java.lang.String.format;\n",
    "\n",
    "\n",
    "class SupervisorAgent implements NodeAction<State> {\n",
    "\n",
    "    static class Router {\n",
    "        @Description(\"Worker to route to next. If no workers needed, route to FINISH.\")\n",
    "        String next;\n",
    "\n",
    "        @Override\n",
    "        public String toString() {\n",
    "            return format( \"Router[next: %s]\",next);\n",
    "        }\n",
    "    }\n",
    "\n",
    "    interface Service {\n",
    "        @SystemMessage( \"\"\"\n",
    "                You are a supervisor tasked with managing a conversation between the following workers: {{members}}.\n",
    "                Given the following user request, respond with the worker to act next.\n",
    "                Each worker will perform a task and respond with their results and status.\n",
    "                When finished, respond with FINISH.\n",
    "                \"\"\")\n",
    "        Router evaluate(@V(\"members\") String members, @dev.langchain4j.service.UserMessage String userMessage);\n",
    "    }\n",
    "\n",
    "    final Service service;\n",
    "    public final String[] members = {\"researcher\", \"coder\" };\n",
    "\n",
    "    public SupervisorAgent(ChatModel model ) {\n",
    "\n",
    "        service = AiServices.create( Service.class, model );\n",
    "    }\n",
    "\n",
    "    @Override\n",
    "    public Map<String, Object> apply(State state) throws Exception {    \n",
    "        \n",
    "        var message = state.lastMessage().orElseThrow();\n",
    "\n",
    "        var text = switch( message.type() ) {\n",
    "            case USER -> ((UserMessage)message).singleText();\n",
    "            case AI -> ((AiMessage)message).text();\n",
    "            default -> throw new IllegalStateException(\"unexpected message type: \" + message.type() );\n",
    "        };\n",
    "\n",
    "        var m = String.join(\",\", members);\n",
    "        \n",
    "        var result = service.evaluate( m, text );\n",
    "        \n",
    "        return Map.of( \"next\", result.next );\n",
    "    }\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Researcher Agent\n",
    "\n",
    "⚠️ Since this is just a POC the tool is mocked ⚠️"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ResearchAgent implements NodeAction<State> {\n",
    "    static class Tools {\n",
    "\n",
    "        @Tool(\"\"\"\n",
    "        Use this to perform a research over internet\n",
    "        \"\"\")\n",
    "        String search(@P(\"internet query\") String query) {\n",
    "            log.info( \"search query: '{}'\", query );\n",
    "            return \"\"\"\n",
    "            the games will be in Italy at Cortina '2026\n",
    "            \"\"\";\n",
    "        }\n",
    "    }\n",
    "\n",
    "    interface Service {\n",
    "        String search(@dev.langchain4j.service.UserMessage  String query);\n",
    "    }\n",
    "\n",
    "    final Service service;\n",
    "\n",
    "    public ResearchAgent( ChatModel model ) {\n",
    "        service = AiServices.builder( Service.class )\n",
    "                        .chatModel(model)\n",
    "                        .tools( new Tools() )\n",
    "                        .build();\n",
    "    }\n",
    "    @Override\n",
    "    public Map<String, Object> apply(State state) throws Exception {\n",
    "        var message = state.lastMessage().orElseThrow();\n",
    "        var text = switch( message.type() ) {\n",
    "            case USER -> ((UserMessage)message).singleText();\n",
    "            case AI -> ((AiMessage)message).text();\n",
    "            default -> throw new IllegalStateException(\"unexpected message type: \" + message.type() );\n",
    "        };\n",
    "        var result = service.search( text );\n",
    "        return Map.of( \"messages\", AiMessage.from(result) );\n",
    "\n",
    "    }\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Coder Agent\n",
    "\n",
    "⚠️ Since this is just a POC the tool is mocked ⚠️"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CoderAgent implements NodeAction<State> {\n",
    "    static class Tools {\n",
    "\n",
    "        @Tool(\"\"\"\n",
    "            Use this to execute java code and do math. If you want to see the output of a value,\n",
    "            you should print it out with `System.out.println(...);`. This is visible to the user.\"\"\")\n",
    "        String search(@P(\"coder request\") String request) {\n",
    "            log.info( \"CoderTool request: '{}'\", request );\n",
    "            return \"\"\"\n",
    "            2\n",
    "            \"\"\";\n",
    "        }\n",
    "    }\n",
    "\n",
    "    interface Service {\n",
    "        String evaluate(@dev.langchain4j.service.UserMessage String code);\n",
    "    }\n",
    "\n",
    "    final Service service;\n",
    "\n",
    "    public CoderAgent( ChatModel model ) throws Exception {\n",
    "        service = AiServices.builder( Service.class )\n",
    "                .chatModel(model)\n",
    "                .tools( new Tools() )\n",
    "                .build();\n",
    "    }\n",
    "    @Override\n",
    "    public Map<String, Object> apply(State state) {\n",
    "        var message = state.lastMessage().orElseThrow();\n",
    "        var text = switch( message.type() ) {\n",
    "            case USER -> ((UserMessage)message).singleText();\n",
    "            case AI -> ((AiMessage)message).text();\n",
    "            default -> throw new IllegalStateException(\"unexpected message type: \" + message.type() );\n",
    "            };\n",
    "        var result = service.evaluate( text );\n",
    "        return Map.of( \"messages\", AiMessage.from(result) );\n",
    "\n",
    "    }\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Initialize OLLAMA Models**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "import dev.langchain4j.model.ollama.OllamaChatModel;\n",
    "\n",
    "var model = OllamaChatModel.builder()\n",
    "    .baseUrl( \"http://localhost:11434\" )\n",
    "    .temperature(0.0)\n",
    "    .logRequests(true)\n",
    "    .logResponses(true)\n",
    "    .format( \"json\" )\n",
    "    .modelName(\"deepseek-r1:14b\")\n",
    "    .build();\n",
    "\n",
    "var modelWithTool = OllamaChatModel.builder()\n",
    "    .baseUrl( \"http://localhost:11434\" )\n",
    "    .temperature(0.0)\n",
    "    .logRequests(true)\n",
    "    .logResponses(true)\n",
    "    .modelName(\"llama3.1:latest\")\n",
    "    .build();\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define Graph\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "import org.bsc.langgraph4j.StateGraph;\n",
    "import org.bsc.langgraph4j.utils.EdgeMappings;\n",
    "import static org.bsc.langgraph4j.StateGraph.END;\n",
    "import static org.bsc.langgraph4j.StateGraph.START;\n",
    "import static org.bsc.langgraph4j.action.AsyncEdgeAction.edge_async;\n",
    "import static org.bsc.langgraph4j.action.AsyncNodeAction.node_async;\n",
    "\n",
    "\n",
    "var supervisor = new SupervisorAgent(model);\n",
    "var coder = new CoderAgent(modelWithTool);\n",
    "var researcher = new ResearchAgent(modelWithTool);\n",
    "\n",
    "var workflow = new StateGraph<>( State.SCHEMA, new StateSerializer() )\n",
    ".addNode( \"supervisor\", node_async(supervisor)) \n",
    ".addNode( \"coder\", node_async(coder) )\n",
    ".addNode( \"researcher\",node_async(researcher) )\n",
    ".addEdge( START, \"supervisor\")\n",
    ".addConditionalEdges( \"supervisor\",\n",
    "        edge_async( state ->\n",
    "            state.next().orElseThrow()\n",
    "        ),\n",
    "        EdgeMappings.builder()\n",
    "        .to( \"coder\" )\n",
    "        .to( \"researcher\")\n",
    "        .toEND( \"FINISH\")\n",
    "        .build())\n",
    ".addEdge( \"coder\", \"supervisor\")\n",
    ".addEdge( \"researcher\", \"supervisor\")\n",
    ";\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Display StateGraph**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "var representation = workflow.getGraph( GraphRepresentation.Type.PLANTUML, \"sub graph\", false );\n",
    "\n",
    "displayDiagram( representation );"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Run Graph (Supervisor -> coder)** "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "START \n",
      "NodeOutput{node=__START__, state={messages=[UserMessage { name = null contents = [TextContent { text = \"what are the result of 1 + 1 ?\" }] }]}} \n",
      "CoderTool request: 'System.out.println(1+1);' \n",
      "NodeOutput{node=supervisor, state={next=coder, messages=[UserMessage { name = null contents = [TextContent { text = \"what are the result of 1 + 1 ?\" }] }]}} \n",
      "NodeOutput{node=coder, state={next=coder, messages=[UserMessage { name = null contents = [TextContent { text = \"what are the result of 1 + 1 ?\" }] }, AiMessage { text = \"The result of 1 + 1 is 2.\" toolExecutionRequests = [] }]}} \n",
      "NodeOutput{node=supervisor, state={next=FINISH, messages=[UserMessage { name = null contents = [TextContent { text = \"what are the result of 1 + 1 ?\" }] }, AiMessage { text = \"The result of 1 + 1 is 2.\" toolExecutionRequests = [] }]}} \n",
      "NodeOutput{node=__END__, state={next=FINISH, messages=[UserMessage { name = null contents = [TextContent { text = \"what are the result of 1 + 1 ?\" }] }, AiMessage { text = \"The result of 1 + 1 is 2.\" toolExecutionRequests = [] }]}} \n"
     ]
    }
   ],
   "source": [
    "var graph = workflow.compile();\n",
    "\n",
    "for( var event : graph.stream( Map.of( \"messages\", UserMessage.from(\"what are the result of 1 + 1 ?\"))) ) {\n",
    "\n",
    "    log.info( \"{}\", event );\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Run Graph (Supervisor -> researcher)** "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "START \n",
      "NodeOutput{node=__START__, state={messages=[UserMessage { name = null contents = [TextContent { text = \"where are next winter olympic games ?\" }] }]}} \n",
      "search query: 'next winter olympic games location' \n",
      "NodeOutput{node=supervisor, state={next=researcher, messages=[UserMessage { name = null contents = [TextContent { text = \"where are next winter olympic games ?\" }] }]}} \n",
      "NodeOutput{node=researcher, state={next=researcher, messages=[UserMessage { name = null contents = [TextContent { text = \"where are next winter olympic games ?\" }] }, AiMessage { text = \"The next Winter Olympic Games will take place in Cortina d'Ampezzo, Italy in 2026.\" toolExecutionRequests = [] }]}} \n",
      "NodeOutput{node=supervisor, state={next=FINISH, messages=[UserMessage { name = null contents = [TextContent { text = \"where are next winter olympic games ?\" }] }, AiMessage { text = \"The next Winter Olympic Games will take place in Cortina d'Ampezzo, Italy in 2026.\" toolExecutionRequests = [] }]}} \n",
      "NodeOutput{node=__END__, state={next=FINISH, messages=[UserMessage { name = null contents = [TextContent { text = \"where are next winter olympic games ?\" }] }, AiMessage { text = \"The next Winter Olympic Games will take place in Cortina d'Ampezzo, Italy in 2026.\" toolExecutionRequests = [] }]}} \n"
     ]
    }
   ],
   "source": [
    "var graph = workflow.compile();\n",
    "\n",
    "for( var event : graph.stream( Map.of( \"messages\", UserMessage.from(\"where are next winter olympic games ?\"))) ) {\n",
    "\n",
    "    log.info( \"{}\", event );\n",
    "}\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Java (rjk 2.2.0)",
   "language": "java",
   "name": "rapaio-jupyter-kernel"
  },
  "language_info": {
   "codemirror_mode": "java",
   "file_extension": ".jshell",
   "mimetype": "text/x-java-source",
   "name": "java",
   "nbconvert_exporter": "script",
   "pygments_lexer": "java",
   "version": "22.0.2+9-70"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
